AWS CloudFormationを使用してAmazon Lexのバージョンを作成した際の挙動を確認してみた

AWS CloudFormationを使用してAmazon Lexのバージョンを作成した際の挙動を確認してみた

Clock Icon2024.07.23

はじめに

AWSドキュメントには、AWS CloudFormationを使用したAmazon Lexのバージョン管理に関する詳細な記載がなく、その挙動をイメージすることが難しかったです。そこで今回は、CloudFormationを使用してLexのバージョンを作成する際の挙動を実際に確認してみました。

Amazon Lexのバージョンとエイリアスについての詳細は、以下のリンクをご参照ください。

https://dev.classmethod.jp/articles/amazon-lex-versioning-and-draft-version/

挙動を確認するため、以下のCloudFormationテンプレートを使用してLexボットを作成します。
このボットは、Amazon Connectから呼び出され、特定の製品カテゴリーを聞き取る機能を持っています。
また、音声をS3バケットに保存し、Lexでの文字起こし内容をCloudWatch Logsにログとして記録します。

バージョンは作成せず、エイリアスは作成するテンプレートです。

AWSTemplateFormatVersion: 2010-09-09
Description: Amazon Lex Bot with Intent creation (Japanese)

Parameters:
  LexBotName:
    Type: String
    Default: cm-hirai-product-name
    Description: The name of the Lex bot
  SlotName:
    Type: String
    Default: name
    Description: The name of the slot
  CustomSlotTypeName:
    Type: String
    Default: productname
    Description: The name of the custom slot type
  AliasName:
    Type: String
    AllowedValues:
      - dev
      - prd
    Default: dev
    Description: The alias name for the Lex bot

Resources:
  MyLexBot:
    Type: AWS::Lex::Bot
    Properties:
      Name: !Ref LexBotName
      Description: !Ref LexBotName
      DataPrivacy: 
        ChildDirected: false
      IdleSessionTTLInSeconds: 300
      RoleArn: !GetAtt LexBotRole.Arn
      BotLocales:
        - LocaleId: ja_JP  
          NluConfidenceThreshold: 0.40
          VoiceSettings:
            VoiceId: Kazuha
          Intents:
            - Name: FallbackIntent
              ParentIntentSignature: AMAZON.FallbackIntent
            - Name: ProductName
              SampleUtterances:
                - Utterance: '{name}'
                - Utterance: 商材名は、{name}です。
                - Utterance: '{name}です。'
                - Utterance: 聞きたいのは、{name}についてです。
              Slots:
                - Name: !Ref SlotName
                  SlotTypeName: !Ref CustomSlotTypeName
                  ValueElicitationSetting:
                    SlotConstraint: Required
                    PromptSpecification:
                      MaxRetries: 3
                      MessageGroupsList:
                        - Message: 
                            PlainTextMessage: 
                              Value: 商材名をお伝え下さい。
              IntentConfirmationSetting: 
                PromptSpecification:
                  MaxRetries: 3
                  MessageGroupsList:
                    - Message:
                        PlainTextMessage:
                          Value: 商材名は、{name}、ですね。よろしければ、はい、と、異なる場合、いいえ、とお伝え下さい。
                ## 確認プロンプトに NOと伝えた場合のボットのプロンプト
                # DeclinationResponse:
                #   MessageGroupsList:
                #     - Message:
                #         PlainTextMessage:
                #           Value: 正しく商材名を聞き取れず、申し訳ございません。
              ## 応答を閉じる
              # IntentClosingSetting:
              #   ClosingResponse:
              #     MessageGroupsList:
              #       - Message:
              #           PlainTextMessage:
              #             Value: 担当者にお繋ぎします
              SlotPriorities:
                - Priority: 1
                  SlotName: !Ref SlotName
          SlotTypes:
            - Name: !Ref CustomSlotTypeName
              ValueSelectionSetting:
                ResolutionStrategy: ORIGINAL_VALUE
              SlotTypeValues:
                - SampleValue: 
                    Value: Wifi
                - SampleValue: 
                    Value: ウォーターサーバー
                - SampleValue: 
                    Value: スマートホームデバイス
                - SampleValue: 
                    Value: 電動自転車
      TestBotAliasSettings:
        BotAliasLocaleSettings:
          - LocaleId: ja_JP
            BotAliasLocaleSetting:
              Enabled: true
              ## Lambdaを利用する場合
              # CodeHookSpecification:
              #   LambdaCodeHook:
              #     LambdaArn: !GetAtt LambdaFunction.Arn
        ConversationLogSettings:
          TextLogSettings:
            - Enabled: true
              Destination: 
                CloudWatch: 
                  CloudWatchLogGroupArn: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:${CloudWatchLogGroup}"
                  LogPrefix: TestBotAlias/
          AudioLogSettings:
            - Enabled: true
              Destination:
                S3Bucket:
                  S3BucketArn: !GetAtt S3Bucket.Arn
                  LogPrefix: TestBotAlias/
  # バージョンを利用する場合
  # BotVersion1:
  #   Type: AWS::Lex::BotVersion
  #   Properties:
  #     BotId: !Ref MyLexBot
  #     BotVersionLocaleSpecification:
  #       - LocaleId: ja_JP
  #         BotVersionLocaleDetails:
  #           SourceBotVersion: DRAFT
  MyLexBotAlias:
    Type: AWS::Lex::BotAlias
    Properties:
      BotAliasName: dev
      BotId: !Ref MyLexBot
      # バージョンを利用する場合
      # BotVersion: !GetAtt BotVersion1.BotVersion
      ConversationLogSettings:
        TextLogSettings:
          - Enabled: true
            Destination: 
              CloudWatch: 
                CloudWatchLogGroupArn: !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:${CloudWatchLogGroup}"
                LogPrefix: dev/
        AudioLogSettings:
          - Enabled: true
            Destination:
              S3Bucket:
                S3BucketArn: !GetAtt S3Bucket.Arn
                LogPrefix: dev/
  LexBotRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service: lexv2.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: !Sub ${LexBotName}-policy
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - polly:SynthesizeSpeech
                Resource: '*'
              - Effect: Allow
                Action: 
                  - s3:PutObject
                Resource: 
                  - !Sub "arn:aws:s3:::${S3Bucket}/*"
              - Effect: Allow
                Action:
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: 
                  - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:${CloudWatchLogGroup}:*"
  CloudWatchLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub "/aws/lex/${LexBotName}"
      RetentionInDays: 365
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketName: !Ref LexBotName

パラメータは、以下の内容でスタックを作成します。
cm-hirai-screenshot 2024-07-18 9.39.37

スタックを作成すると、バージョンは、テンプレートに記載していませんが、ドラフトバージョンは自動作成されます。
cm-hirai-screenshot 2024-07-18 11.33.11
バージョンは作成していないため、エイリアスdevは作成されますが、バージョンには関連付けられてはいません。
cm-hirai-screenshot 2024-07-18 11.33.16

先に結論

CloudFormationを使用してLexのバージョンを作成した際の主な挙動は、以下の通りです

  • 新しいバージョンを追加しつつ、元のバージョンを残す場合は、テンプレートにリソースとしてボットバージョンを追加する必要がある
    • テンプレートのコード量がどんどん増えてしまう
  • 元のバージョンが不要な場合は、テンプレートのバージョンの論理名を変更するだけでよい
  • CloudFormationでバージョンを作成する場合、ボットの構築(Build)が自動的に行われる

上記の実際の挙動を確認してみます。

CloudFormationでバージョン1を作成

CloudFormationでバージョンを作成してエイリアスdevを関連付けるには、テンプレートに以下のように変更を加える必要があります。

以下の通りコメントアウトしました。

  # バージョンを利用する場合
-  # BotVersion1:
-  #   Type: AWS::Lex::BotVersion
-  #   Properties:
-  #     BotId: !Ref MyLexBot
-  #     BotVersionLocaleSpecification:
-  #       - LocaleId: ja_JP
-  #         BotVersionLocaleDetails:
-  #           SourceBotVersion: DRAFT
+  BotVersion1:
+    Type: AWS::Lex::BotVersion
+    Properties:
+      BotId: !Ref MyLexBot
+      BotVersionLocaleSpecification:
+        - LocaleId: ja_JP
+          BotVersionLocaleDetails:
+            SourceBotVersion: DRAFT
  MyLexBotAlias:
    Type: AWS::Lex::BotAlias
    Properties:
      BotAliasName: dev
      BotId: !Ref MyLexBot
      # バージョンを利用する場合
-      # BotVersion: !GetAtt BotVersion1.BotVersion
+      BotVersion: !GetAtt BotVersion1.BotVersion

この変更を適用してスタックを更新します。

変更セットのプレビューは以下のようになります

cm-hirai-screenshot 2024-07-18 11.35.25

バージョン1が作成されました。
cm-hirai-screenshot 2024-07-18 11.36.37
バージョン1がエイリアスdevに関連付けられました。
cm-hirai-screenshot 2024-07-18 11.36.43

手動でのバージョン作成との違い

手動でバージョンを作成する場合、以下のような状況では、ドラフトバージョンを構築(Build)してからバージョンを作成する必要があります

  • ボット作成直後にバージョンを作成する場合
  • ボットを作成し構築後、設定変更をしたため、その変更内容を反映したバージョンを作成する場合

ドラフトバージョンを構築せずにバージョンを作成すると、そのバージョンには設定変更内容が反映されません。また、ボット作成直後に構築しない場合、ボット自体利用することができません。

cm-hirai-screenshot 2024-07-22 10.53.48
cm-hirai-screenshot 2024-07-22 10.54.40

一方、CloudFormationでバージョンを作成する場合、スタック更新中に自動で構築(Build)が行われます。

cm-hirai-screenshot 2024-07-22 10.33.53
CloudFormationでボット作成時(バージョンは未作成)
cm-hirai-screenshot 2024-07-22 10.54.40
CloudFormationでバージョンを作成するようスタック更新後、自動で構築される

CloudFormationでバージョン2を作成

Lexボットの設定(例えば、インテントや発話例)を修正したと仮定し、新しいバージョン2を作成してエイリアスdevに関連付けるには、テンプレートに以下の対応が必要です。

  • 新しいリソース(BotVersion2)を追加します。
  • エイリアスdevの関連付けを、バージョン1からバージョン2に変更します。

具体的には、CloudFormationテンプレートに以下のような変更を加えます。

  BotVersion1:
    Type: AWS::Lex::BotVersion
    Properties:
      BotId: !Ref MyLexBot
      BotVersionLocaleSpecification:
        - LocaleId: ja_JP
          BotVersionLocaleDetails:
            SourceBotVersion: DRAFT
+  BotVersion2:
+    Type: AWS::Lex::BotVersion
+    Properties:
+      BotId: !Ref MyLexBot
+      BotVersionLocaleSpecification:
+        - LocaleId: ja_JP
+          BotVersionLocaleDetails:
+            SourceBotVersion: DRAFT
  MyLexBotAlias:
    Type: AWS::Lex::BotAlias
    Properties:
      BotAliasName: dev
      BotId: !Ref MyLexBot
      # バージョンを利用する場合
-      BotVersion: !GetAtt BotVersion1.BotVersion
+      BotVersion: !GetAtt BotVersion2.BotVersion

上記の変更を適用してスタックを更新します。

変更セットのプレビューは以下のようになります
cm-hirai-screenshot 2024-07-18 11.46.37

バージョン2が作成されました。
cm-hirai-screenshot 2024-07-18 11.47.27
バージョン2がエイリアスdevに関連付けられました。
cm-hirai-screenshot 2024-07-18 11.47.35

CloudFormationでバージョン1を削除

次に、テンプレートからバージョン1を以下のように削除します。

-  BotVersion1:
-    Type: AWS::Lex::BotVersion
-    Properties:
-      BotId: !Ref MyLexBot
-      BotVersionLocaleSpecification:
-        - LocaleId: ja_JP
-          BotVersionLocaleDetails:
-            SourceBotVersion: DRAFT

上記の変更を適用してスタックを更新します。

変更セットのプレビューは以下のようになります

cm-hirai-screenshot 2024-07-18 11.50.30

バージョン1が削除されました。
cm-hirai-screenshot 2024-07-18 11.51.48

論理名を変える

Lexボットの設定を再度変更したと仮定し、CloudFormationテンプレート内のバージョン2の論理名を変更した場合、バージョン3が作成されます。この動作を確認してみましょう。

以下の通り、論理名を変更します。

+  BotVersion3:
-  BotVersion2:
    Type: AWS::Lex::BotVersion
    Properties:
      BotId: !Ref MyLexBot
      BotVersionLocaleSpecification:
        - LocaleId: ja_JP
          BotVersionLocaleDetails:
            SourceBotVersion: DRAFT
  MyLexBotAlias:
    Type: AWS::Lex::BotAlias
    Properties:
      BotAliasName: dev
      BotId: !Ref MyLexBot
      # バージョンを利用する場合
+      BotVersion: !GetAtt BotVersion3.BotVersion
-      BotVersion: !GetAtt BotVersion2.BotVersion

上記の変更を適用してスタックを更新します。

変更セットのプレビューは以下のようになります

cm-hirai-screenshot 2024-07-18 11.55.45

バージョン2が削除され、バージョン3が作成されエイリアスdevに関連付けされました。

cm-hirai-screenshot 2024-07-18 11.58.10
cm-hirai-screenshot 2024-07-18 11.58.04

以上の動作から、次のことが分かりました

  • 新しいバージョンを追加しつつ、元のバージョンを残す場合は、テンプレートにリソースとしてボットバージョンを追加する必要がある
    • テンプレートのコード量がどんどん増えてしまう
  • 元のバージョンが不要な場合は、テンプレートのバージョンの論理名を変更するだけでよい
  • CloudFormationでバージョンを作成する場合、ボットの構築(Build)が自動的に行われる

元のバージョンを残す必要がある場合、テンプレートのコード量がどんどん増えてしまいますので、今後、代替案を考えてみます。

最後に

本記事では、AWS CloudFormationを使用してAmazon Lexのバージョンを作成する際の挙動確認しました。

特に注意すべき点として、各バージョンを保持する必要がある場合は、CloudFormationテンプレートにコードを追加する必要があります。
これにより、テンプレートが複雑化する可能性があります。

参考になれば幸いです。

参考

https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/AWS_Lex.html

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.